/*
 * Created on Feb 4, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell.query;

import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import edu.mit.simile.SimileUtilities;

/**
 * @author dfhuynh
 */
public class Query {
	protected List		m_restrictions;
	protected List		m_orders;
	protected SortedSet	m_explicitFacets;
	protected List		m_others;
	protected int		m_restrictorID;
	
	final static public String s_restrictionPrefix = "-";
	final static public String s_orderPrefix = "~";
	final static public String s_explicitFacetPrefix = "+";
	
	protected Query() {
		m_restrictions = new ArrayList();
		m_orders = new ArrayList();
		m_explicitFacets = new TreeSet();
		m_others = new ArrayList();
	}
	
	public Query(Query q) {
		m_restrictions = new ArrayList(q.m_restrictions);
		m_orders = new ArrayList(q.m_orders);
		m_explicitFacets = new TreeSet(q.m_explicitFacets);
		m_others = new ArrayList(q.m_others);
	}
	
	public Query(String query) {
		this();
		
		parse(query);
	}
	
	public Query(URL url) {
		this();
		
		parse(url.getQuery());
	}
	
	public Query dup() {
		return new Query(this);
	}
	
	public void setParameter(String name, String value) {
		removeParameter(name);
		
		m_others.add(new QueryTerm(name, value));
	}

	public void removeParameter(String name) {
		Iterator i = m_others.iterator();
		while (i.hasNext()) {
			QueryTerm t = (QueryTerm) i.next();
			
			if (t.getName().equals(name)) {
				i.remove();
			}
		}
	}
	
	public void removeOtherParameters() {
		m_others.clear();
	}

	public String getFirstParamValue(String name) {
		Iterator i = m_others.iterator();
		while (i.hasNext()) {
			QueryTerm t = (QueryTerm) i.next();
			
			if (t.getName().equals(name)) {
				return t.getValue();
			}
		}
		
		return null;
	}
	
	public List getRestrictions() {
		return m_restrictions;
	}
	
	public void clearRestrictions() {
		m_restrictions.clear();
	}
	
	public void addRestriction(
		String projectorName, 
		String projectorParameter,
		String bucketerName,
		String bucketerParameter
	) {
		m_restrictions.add(
			new Restriction(
				m_restrictorID++,
				projectorName,
				projectorParameter,
				bucketerName,
				bucketerParameter
			)
		);
	}
	
	public void removeRestriction(int id) {
		Iterator i = m_restrictions.iterator();
		while (i.hasNext()) {
			Restriction r = (Restriction) i.next();
			
			if (r.m_id == id) {
				i.remove();
			}
		}
	}
	
	public void addExplicitFacet(String propertyURI) {
		m_explicitFacets.add(propertyURI);
	}

	public void removeExplicitFacet(String propertyURI) {
		m_explicitFacets.remove(propertyURI);
	}
	
	public void clearExplicitFacets() {
		m_explicitFacets.clear();
	}
	
	public SortedSet getExplicitFacets() {
		return m_explicitFacets;
	}

	public List getOrders() {
		return m_orders;
	}

	public void clearOrders() {
		m_orders.clear();
	}
	
	public void addOrder(
		String projectorName, 
		String projectorParameter,
		String comparatorName,
		String comparatorParameter,
		boolean ascending
	) {
		m_orders.add(
			new Order(
				projectorName,
				projectorParameter,
				comparatorName,
				comparatorParameter,
				ascending
			)
		);
	}

	final static public char s_separator = ';';
	
	public String toEscapedURLQueryString() {
		return escapeSeparator(toURLQueryString());
	}
	
	public String toURLQueryString() {
		String s = "";
		
		Iterator i = m_others.iterator();
		while (i.hasNext()) {
			QueryTerm t = (QueryTerm) i.next();

			s += "&" + URLEncoder.encode(t.getName()) + 
				"=" + URLEncoder.encode(t.getValue());
		}
		
		i = m_restrictions.iterator();
		while (i.hasNext()) {
			Restriction r = (Restriction) i.next();
			
			s += "&" + s_restrictionPrefix + "=" + URLEncoder.encode(
				escapeSeparator(escapeLongwell(r.m_projectorName)) + s_separator + 
				escapeSeparator(r.m_projectorParameter) + s_separator +
				escapeSeparator(escapeLongwell(r.m_bucketerName)) + s_separator + 
				escapeSeparator(r.m_bucketerParameter)
			);
		}

		i = m_orders.iterator();
		while (i.hasNext()) {
			Order o = (Order) i.next();
			
			s += "&" + s_orderPrefix + "=" + URLEncoder.encode(
				escapeSeparator(escapeLongwell(o.m_projectorName)) + s_separator + 
				escapeSeparator(o.m_projectorParameter) + s_separator +
				escapeSeparator(escapeLongwell(o.m_comparatorName)) + s_separator + 
				escapeSeparator(o.m_comparatorParameter) + s_separator +
				(o.m_ascending ? "a" : "d")
			);
		}

		i = m_explicitFacets.iterator();
		while (i.hasNext()) {
			s += "&" + URLEncoder.encode(s_explicitFacetPrefix) + "=" + 
				URLEncoder.encode((String) i.next());
		}
		return s.length() > 0 ? s.substring(1) : s; // skip the beginning &
	}
	
	protected void parse(String query) {

		if (query == null) {
			return;
		}
		
		String[] params = query.split("&");
		for (int i = 0; i < params.length; i++) {
			String 	param = params[i];
			int 	equalIndex = param.indexOf('=');
			
			if (equalIndex >= 0) {
				String	name = URLDecoder.decode(param.substring(0, equalIndex));
				String	value = URLDecoder.decode(param.substring(equalIndex + 1));
				
				if (name.startsWith(s_restrictionPrefix)) {
					parseRestriction(value);
				} else if (name.startsWith(s_orderPrefix)) {
					parseOrder(value);
				} else if (name.startsWith(s_explicitFacetPrefix)) {
					parseExplicitFacet(value);
				} else {
					m_others.add(new QueryTerm(name, value));
				}
			}
		}
	}
	
	protected void parseRestriction(String s) {
		String[] ses = s.split(Character.toString(s_separator));
		
		String projectorName = unescapeSeparator(unescapeLongwell(ses[0]));
		String projectorParameter = unescapeSeparator(ses.length > 1 ? ses[1] : ""); 
		String bucketerName = unescapeSeparator(ses.length > 2 ? unescapeLongwell(ses[2]) : ""); 
		String bucketerParameter = unescapeSeparator(ses.length > 3 ? ses[3] : "");
		
		addRestriction(
			projectorName,
			projectorParameter,
			bucketerName,
			bucketerParameter
		);
	}
	
	protected void parseOrder(String s) {
		String[] ses = s.split(Character.toString(s_separator));
		
		String projectorName = unescapeSeparator(unescapeLongwell(ses[0]));
		String projectorParameter = unescapeSeparator(ses.length > 1 ? ses[1] : ""); 
		String comparatorName = unescapeSeparator(ses.length > 2 ? unescapeLongwell(ses[2]) : ""); 
		String comparatorParameter = unescapeSeparator(ses.length > 3 ? ses[3] : "");
		boolean ascending = ses.length > 4 ? !ses[4].equals("d") : true;
		
		m_orders.add(
			new Order(
				projectorName,
				projectorParameter,
				comparatorName,
				comparatorParameter,
				ascending
			)
		);
	}
	
	protected void parseExplicitFacet(String s) {
		addExplicitFacet(s);
	}
	
	protected String escapeSeparator(String s) {
		return s == null ? "" : SimileUtilities.escape(s, s_separator, '`', 's');
	}
	
	protected String unescapeSeparator(String s) {
		return s == null ? "" : SimileUtilities.unescape(s, s_separator, '`', 's');
	}
	
	static public String escapeLongwell(String s) {
		return s == null ? null :
			s.replaceAll("edu\\.mit\\.simile\\.longwell\\.query", "@lwq");
	}
	static public String unescapeLongwell(String s) {
		return s == null ? null :
			s.replaceAll("@lwq", "edu.mit.simile.longwell.query");
	}
}
